/*
 * Copyright 2016 Facebook, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#ifndef THRIFT_FATAL_REFLECTION_H_
#define THRIFT_FATAL_REFLECTION_H_ 1

#include <thrift/lib/cpp2/fatal/internal/reflection-inl-pre.h>

#include <fatal/type/convert.h>
#include <fatal/type/enum.h>
#include <fatal/type/foreach.h>
#include <fatal/type/get.h>
#include <fatal/type/get_type.h>
#include <fatal/type/list.h>
#include <fatal/type/map.h>
#include <fatal/type/pair.h>
#include <fatal/type/registry.h>
#include <fatal/type/search.h>
#include <fatal/type/traits.h>
#include <fatal/type/transform.h>
#include <fatal/type/variant_traits.h>

#include <type_traits>

#include <cstdint>

namespace apache { namespace thrift {

/**
 * READ ME FIRST: this file is divided into sections for each specific
 * reflection API.
 *
 * To quickly navigate this file, look for the string "SECTION: " without the
 * quotes.
 *
 * Note that the compile-time reflection API depends on metadata that's
 * generated by the Thrift compiler. In order to have this metadata available,
 * there are three simple but necessary steps:
 *
 *  - enable code generation for `cpp2` as the target language;
 *  - enable `fatal` as one of the `thrift_cpp2_options` flags;
 *  - include the appropriate file containing the metadata (say your module is
 *    defined in `some_dir/Module.thrift`, corresponding files can be included
 *    from `some_dir/gen-cpp2/`):
 *
 *      - Module_fatal.h: general module metadata
 *      - Module_fatal_struct.h: metadata for all structures
 *      - Module_fatal_enum.h: metadata for all enumerations
 *      - Module_fatal_union.h: metadata for all variants (Thrift unions)
 *      - Module_fatal_types.h: convenience header that includes metadata for
 *        modules, structures, enumerations and variants
 *      - Module_fatal_all.h: convenience header that includes all the
 *        metadata generated by the Thrift compiler
 *
 * Thrift breaks the metadata up in several files to help reduce compilation
 * times by including only what's needed.
 *
 * @author: Marcelo Juchem <marcelo@fb.com>
 */

//////////////////////////////////////////////
// SECTION: IMPORTANT NOTES AND CONVENTIONS //
//////////////////////////////////////////////

/**
 * NOTE ON COMPILE-TIME STRINGS: many strings found in the Thrift file are
 * converted to compile-time strings in the form of a `fatal::sequence`
 * of `char`.
 *
 * They are often represented as general C++ identifiers. Not all strings are
 * directly representable as C++ identifiers though, given that not all
 * characters are accepted as identifier names, only [_a-zA-Z0-9]. When that's
 * the case, the invalid characters are replaced by an underscode (_).
 *
 * Names starting with numbers are prefixed with 's_'. For example, the string
 * "42 is it" could be represented by the identifier 's_42_is_it'.
 *
 * Collisions are solved by appending a positive integer starting at 1 and
 * growing by 1 per collision, in the order the identifiers appear. For
 * instance, say there are three strings "a_", "a " and "a.". "a_" could be
 * represented by the identifier 'a_' while "a " could be represented by 'a_1'
 * and "a." could be 'a_2'.
 *
 * @author: Marcelo Juchem <marcelo@fb.com>
 */

////////////////////////////////////////////
// SECTION: TYPE ALIASES AND ENUMERATIONS //
////////////////////////////////////////////

/**
 * An alias to the type used by Thrift as a struct's field ID.
 *
 * @author: Marcelo Juchem <marcelo@fb.com>
 */
using field_id_t = std::int16_t;

/**
 * An alias to the type used by Thrift as a type's unique identifier.
 *
 * NOTE: this is a legacy feature and should be avoided on new code.
 *
 * @author: Marcelo Juchem <marcelo@fb.com>
 */
using legacy_type_id_t = std::uint64_t;

/**
 * The type class of a type as declared on a Thrift file.
 *
 * See `reflect_type_class` for more information.
 *
 * @author: Marcelo Juchem <marcelo@fb.com>
 */
struct type_class {
  /**
   * Represents types unknown to the reflection framework.
   *
   * NOTE: if this is the returned type class, there's a good chance that's
   * because it's a custom type with no specialization of the approriate
   * container trait class. See documentation for `string`, `list`, `set`
   * and `map` enum values below.
   *
   * @author: Marcelo Juchem <marcelo@fb.com>
   */
  struct unknown {};

  /**
   * Represents types with no actual data representation. Most commonly `void`.
   *
   * @author: Marcelo Juchem <marcelo@fb.com>
   */
  struct nothing {};

  /**
   * Represents all signed and unsigned integral types, including `bool`.
   *
   * @author: Marcelo Juchem <marcelo@fb.com>
   */
  struct integral {};

  /**
   * Represents all floating point types.
   *
   * @author: Marcelo Juchem <marcelo@fb.com>
   */
  struct floating_point {};

  /**
   * Represents opaque binary data.
   *
   * @author: Marcelo Juchem <marcelo@fb.com>
   */
  struct binary {};

  /**
   * Represents all known string implementations.
   *
   * NOTE: if this is not the type class returned for a string, there's a good
   * chance that's because it's a custom type with no specialization of the
   * string trait class. See documentation for `thrift_string_traits`.
   *
   * @author: Marcelo Juchem <marcelo@fb.com>
   */
  struct string {};

  /**
   * Represents an enum.
   *
   * @author: Marcelo Juchem <marcelo@fb.com>
   */
  struct enumeration {};

  /**
   * Represents an class or structure.
   *
   * @author: Marcelo Juchem <marcelo@fb.com>
   */
  struct structure {};

  /**
   * Represents a variant (or union, as the Thrift IDL grammar goes).
   *
   * @author: Marcelo Juchem <marcelo@fb.com>
   */
  struct variant {};

  /**
   * Represents all known list implementations.
   *
   * NOTE: if this is not the type class returned for a list, there's a good
   * chance that's because it's a custom type with no specialization of the
   * list trait class. See documentation for `thrift_list_traits`.
   *
   * @author: Marcelo Juchem <marcelo@fb.com>
   */
  template <typename ValueTypeClass>
  struct list {
    /**
     * The type class of the elements of this container.
     *
     * @author: Marcelo Juchem <marcelo@fb.com>
     */
    using value_type_class = ValueTypeClass;
  };

  /**
   * Represents all known set implementations.
   *
   * NOTE: if this is not the type class returned for a set, there's a good
   * chance that's because it's a custom type with no specialization of the
   * set trait class. See documentation for `thrift_set_traits`.
   *
   * @author: Marcelo Juchem <marcelo@fb.com>
   */
  template <typename ValueTypeClass>
  struct set {
    /**
     * The type class of the elements of this container.
     *
     * @author: Marcelo Juchem <marcelo@fb.com>
     */
    using value_type_class = ValueTypeClass;
  };

  /**
   * Represents all known map implementations.
   *
   * NOTE: if this is not the type class returned for a map, there's a good
   * chance that's because it's a custom type with no specialization of the
   * map trait class. See documentation for `thrift_map_traits`.
   *
   * @author: Marcelo Juchem <marcelo@fb.com>
   */
  template <typename KeyTypeClass, typename MappedTypeClass>
  struct map {
    /**
     * The type class of the keys of this container.
     *
     * @author: Marcelo Juchem <marcelo@fb.com>
     */
    using key_type_class = KeyTypeClass;

    /**
     * The type class of the mapped elements of this container.
     *
     * @author: Marcelo Juchem <marcelo@fb.com>
     */
    using mapped_type_class = MappedTypeClass;
  };
};

/**
 * Represents whether a field is required to be set in a given structure or not.
 *
 * @author: Marcelo Juchem <marcelo@fb.com>
 */
enum class optionality {
  /**
   * Field is required.
   *
   * @author: Marcelo Juchem <marcelo@fb.com>
   */
  required,
  /**
   * Field is optional.
   *
   * @author: Marcelo Juchem <marcelo@fb.com>
   */
  optional,
  /**
   * Field is optional on the reading side but required on the writing side.
   *
   * @author: Marcelo Juchem <marcelo@fb.com>
   */
  required_of_writer
};

/////////////////////////////
// SECTION: TYPE CLASS API //
/////////////////////////////

/**
 * Returns the type class of a type.
 *
 * To keep compilation times at bay, strings and containers are not detected by
 * default, therefore they will yield `unknown` as their type class. To enable
 * their detection you must include `container_traits.h`. There's also support
 * for containers defined in Folly at `container_traits_folly.h`.
 *
 * See `type_class` for the possible categories.
 *
 * Example:
 *
 *  /////////////////////
 *  // MyModule.thrift //
 *  /////////////////////
 *  namespace cpp2 My.Namespace
 *
 *  struct MyStruct {
 *    1: i32 a
 *    2: string b
 *    3: double c
 *  }
 *
 *  enum MyEnum { a, b, c }
 *
 *  typedef list<string> MyList;
 *
 *  /////////////
 *  // foo.cpp //
 *  /////////////
 *
 *  // yields `type_class::structure`
 *  using result1 = reflect_type_class<MyStruct>;
 *
 *  // yields `type_class::enumeration`
 *  using result2 = reflect_type_class<MyEnum>;
 *
 *  // yields `type_class::list<type_class::string>`
 *  using result3 = reflect_type_class<MyList>;
 *
 *  // yields `type_class::unknown`
 *  using result4 = reflect_type_class<void>;
 *
 * @author: Marcelo Juchem <marcelo@fb.com>
 */
template <typename T>
using reflect_type_class = typename detail::reflect_type_class_impl<T>::type;

/////////////////////////
// SECTION: MODULE API //
/////////////////////////

/**
 * Holds reflection metadata for stuff defined in a Thrift file.
 *
 * NOTE: this class template is only intended to be instantiated by Thrift.
 * Users should ignore the template parameters taken by it and focus simply on
 * the members provided.
 *
 * @author: Marcelo Juchem <marcelo@fb.com>
 */
template <
  typename Name,
  typename Namespaces,
  typename Enums,
  typename Unions,
  typename Structs,
  typename Constants,
  typename Services
>
struct reflected_module {
  /**
   * The name.
   *
   * A `fatal::constant_sequence` (compile-time string) representing the module
   * name.
   *
   * Example:
   *
   *  // MyModule.thrift
   *
   *  namespace cpp2 My.Namespace
   *  struct MyStruct {}
   *
   *  // C++
   *
   *  using info = reflect_module<My::Namespace::MyModule_tags::module>;
   *  using name = typename info::name;
   *  // yields `fatal::constant_sequence<
   *  //   char, 'M', 'y', 'M', 'o', 'd', 'u', 'l', 'e',
   *  // >`
   */
  using name = Name;

  /**
   * The map from language to namespace.
   *
   * A `fatal::map` where the key is a `fatal::sequence`
   * (compile-time string) representing the language, associated with a
   * compile-time string representing the namespace.
   *
   * Example:
   *
   *  // MyModule.thrift
   *
   *  namespace cpp My.NamespaceCpp
   *  namespace cpp2 My.Namespace
   *  namespace java My.Namespace
   *
   *  struct MyStruct {
   *    1: i32 a
   *    2: string b
   *    3: double c
   *  }
   *
   *  // C++
   *
   *  using info = reflect_module<My::Namespace::MyModule_tags::module>;
   *
   *  FATAL_S(cpp, "cpp");
   *  FATAL_S(cpp2, "cpp2");
   *  FATAL_S(java, "java");
   *
   *  // yields `fatal::sequence<
   *  //   char,
   *  //   'M', 'y', ':', ':', 'N', 'a', 'm', 'e',
   *  //   's', 'p', 'a', 'c', 'e', 'C', 'p', 'p'
   *  // >`
   *  using result1 = info::namespaces::get<cpp>;
   *
   *  // yields `fatal::sequence<
   *  //   char, 'M', 'y', ':', ':', 'N', 'a', 'm', 'e', 's', 'p', 'a', 'c', 'e'
   *  // >`
   *  using result2 = info::namespaces::get<cpp2>;
   *
   *  // yields `fatal::sequence<
   *  //   char, 'M', 'y', '.', 'N', 'a', 'm', 'e', 's', 'p', 'a', 'c', 'e'
   *  // >`
   *  using result3 = info::namespaces::get<java>;
   *
   * @author: Marcelo Juchem <marcelo@fb.com>
   */
  using namespaces = Namespaces;

  /**
   * The list of enumerations with reflection metadata available.
   *
   * A `fatal::map` where the key is the type generated by the Thrift
   * compiler for each enumeration, associated with a compile-time string
   * (`fatal::sequence`) representing the enumeration name.
   *
   * Use `fatal::enum_traits` to retrieve reflection information for each
   * enumeration (fatal/type/enum.h).
   *
   * @author: Marcelo Juchem <marcelo@fb.com>
   */
  using enums = Enums;

  /**
   * The list of unions with reflection metadata available.
   *
   * A `fatal::map` where the key is the type generated by the Thrift
   * compiler for each union, associated with a compile-time string
   * (`fatal::sequence`) representing the union name.
   *
   * Use `fatal::variant_traits` to retrieve reflection information for each
   * union (fatal/type/variant_traits.h).
   *
   * @author: Marcelo Juchem <marcelo@fb.com>
   */
  using unions = Unions;

  /**
   * The list of structs with reflection metadata available.
   *
   * A `fatal::map` where the key is the type generated by the Thrift
   * compiler for each struct, associated with a compile-time string
   * (`fatal::sequence`) representing the struct name.
   *
   * @author: Marcelo Juchem <marcelo@fb.com>
   */
  using structs = Structs;

  /**
   * The list of services with reflection metadata available.
   *
   * A `fatal::list` of compile-time strings
   * (`fatal::sequence`) representing each service name.
   *
   * @author: Marcelo Juchem <marcelo@fb.com>
   */
  using services = Services;
};

/**
 * Retrieves reflection metadata (as a `reflected_module`) associated with the
 * given reflection metadata tag.
 *
 * The Thrift compiler generates a reflection metadata tag for each Thrift file
 * named `namespace::thriftfilename_tags::module`.
 *
 * If the given tag does not represent a Thrift module, or if there's no
 * reflection metadata available for it, compilation will fail.
 *
 * See the documentation on `reflected_module` (above) for more information on
 * the returned type.
 *
 * Example:
 *
 *  /////////////////////
 *  // MyModule.thrift //
 *  /////////////////////
 *  namespace cpp2 My.Namespace
 *
 *  enum MyEnum1 { a, b, c }
 *  enum MyEnum2 { x, y, x }
 *
 *  //////////////////
 *  // whatever.cpp //
 *  //////////////////
 *  using info = reflect_module<My::Namespace::MyModule_tags::module>;
 *
 *  // yields `2`
 *  auto result1 = info::enums::size;
 *
 *  // fails compilation
 *  using result2 = reflect_module<void>;
 *
 * @author: Marcelo Juchem <marcelo@fb.com>
 */
template <typename Tag>
using reflect_module = fatal::registry_lookup<
  detail::reflection_metadata_tag,
  Tag
>;

/**
 * Retrieves reflection metadata (as a `reflected_module`) associated with the
 * given reflection metadata tag.
 *
 * The Thrift compiler generates a reflection metadata tag for each Thrift file
 * named `namespace::thriftfilename_tags::module`.
 *
 * If the given tag does not represent a Thrift module, or if there's no
 * reflection metadata available for it, `Default` will be returned.s If
 * `Default` is not specified, it defaults to void.
 *
 * See the documentation on `reflected_module` (above) for more information on
 * the returned type.
 *
 * Example:
 *
 *  /////////////////////
 *  // MyModule.thrift //
 *  /////////////////////
 *  namespace cpp2 My.Namespace
 *
 *  enum MyEnum1 { a, b, c }
 *  enum MyEnum2 { x, y, x }
 *
 *  //////////////////
 *  // whatever.cpp //
 *  //////////////////
 *  using info = try_reflect_module<
 *    My::Namespace::MyModule_tags::module,
 *    void
 *  >;
 *
 *  // yields `2`
 *  auto result1 = info::enums::size;
 *
 *  // yields `void`
 *  using result2 = try_reflect_module<int>;
 *
 * @author: Marcelo Juchem <marcelo@fb.com>
 */
template <typename Tag, typename Default = void>
using try_reflect_module = fatal::try_registry_lookup<
  detail::reflection_metadata_tag,
  Tag,
  Default
>;

/**
 * Tells whether the given type is a tag that represents the reflection metadata
 * of the types declared in a Thrift file.
 *
 * Example:
 *
 *  /////////////////////
 *  // MyModule.thrift //
 *  /////////////////////
 *  namespace cpp2 My.Namespace
 *
 *  struct MyStruct {
 *    1: i32 a
 *    2: string b
 *    3: double c
 *  }
 *
 *  /////////////
 *  // foo.cpp //
 *  /////////////
 *
 *  // yields `std::true_type`
 *  using result1 = is_reflectable_module<MyModule_tags::module>;
 *
 *  // yields `std::false_type`
 *  using result2 = is_reflectable_module<MyStruct>;
 *
 *  // yields `std::false_type`
 *  using result3 = is_reflectable_module<void>;
 *
 * @author: Marcelo Juchem <marcelo@fb.com>
 */
template <typename T>
using is_reflectable_module = std::integral_constant<
  bool,
  !std::is_same<
    try_reflect_module<T, void>,
    void
  >::value
>;

/**
 * Gets the reflection metadata tag for the Thrift file where the type `T` is
 * declared.
 *
 * The type `T` must be either a struct, enum or union.
 *
 * Example:
 *
 *  // MyModule.thrift
 *
 *  namespace cpp2 My.Namespace
 *
 *  struct MyStruct {
 *    1: i32 a
 *    2: string b
 *    3: double c
 *  }
 *
 *  enum MyEnum { a, b, c }
 *
 *  // C++
 *
 *  // yields `My::Namespace::MyModule_tags::module`
 *  using result1 = reflect_module_tag<MyStruct>;
 *
 *  // yields `My::Namespace::MyModule_tags::module`
 *  using result2 = reflect_module_tag<MyEnum>;
 *
 * @author: Marcelo Juchem <marcelo@fb.com>
 */
template <typename T>
using reflect_module_tag = typename detail::reflect_module_tag_selector<
  reflect_type_class<T>,
  T,
  false
>::type;

/**
 * Tries to get the reflection metadata tag for the Thrift file where the type
 * `T` is declared.
 *
e:
 *
 *  // MyModule.thrift
 *
 *  namespace cpp2 My.Namespace
 *
 *  struct MyStruct {
 *    1: i32 a
 *    2: string b
 *    3: double c
 *  }
 *
 *  enum MyEnum { a, b, c }
 *
 *  // C++
 *
 *  // yields `My::Namespace::MyModule_tags::module`
 *  using result1 = reflect_module_tag<MyStruct, void>;
 *
 *  // yields `My::Namespace::MyModule_tags::module`
 *  using result2 = reflect_module_tag<MyEnum, void>;
 *
 *  // yields `void`
 *  using result3 = reflect_module_tag<int, void>;
 *
 * @author: Marcelo Juchem <marcelo@fb.com>
 */
template <typename T, typename Default = void>
using try_reflect_module_tag = typename detail::reflect_module_tag_selector<
  reflect_type_class<T>,
  T,
  true,
  Default
>::type;

/**
 * Holds reflection metadata for annotations.
 *
 * For the examples below, consider code generated for this Thrift file:
 *
 *  /////////////////////
 *  // MyModule.thrift //
 *  /////////////////////
 *  namespace cpp2 My.Namespace
 *
 *  struct MyStruct {
 *    1: i32 a
 *    2: string b
 *    3: double c
 *  } (
 *    some.annotation = "some value",
 *    another.annotation = "another value",
 *  )
 *
 * NOTE: this class template is only intended to be instantiated by Thrift.
 * Users should ignore the template parameters taken by it and focus simply on
 * the members provided.
 *
 * @author: Marcelo Juchem <marcelo@fb.com>
 */
template <typename Metadata>
struct reflected_annotations {
  /**
   * An implementation defined type that provides the names for each annotation
   * key as a member type alias named after the key.
   *
   * These type aliases are used as the key for the `map` member.
   *
   * Look for 'NOTE ON COMPILE-TIME STRINGS' for how the strings are converted
   * to C++ identifiers - caveat: instead of using the order they appear as the
   * precedence for collision resolution, it uses lexicographical order of the
   * keys.
   *
   * Example:
   *
   *  using annotations = reflect_struct<MyStruct>::annotations;
   *
   *  // yields `fatal::sequence<char,
   *  //   's', 'o', 'm', 'e', '.',
   *  //   'a', 'n', 'n', 'o', 't', 'a', 't', 'i', 'o', 'n'
   *  // >`
   *  using result1 = annotations::keys::some_annotation;
   *
   * @author: Marcelo Juchem <marcelo@fb.com>
   */
  using keys = typename Metadata::keys;

  /**
   * An implementation defined type that provides the names for each annotation
   * value as a member type alias named after the key.
   *
   * These type aliases are used as the value for the `map` member.
   *
   * Look for 'NOTE ON COMPILE-TIME STRINGS' for how the strings are converted
   * to C++ identifiers - caveat: instead of using the order they appear as the
   * precedence for collision resolution, it uses lexicographical order of the
   * keys.
   *
   * Example:
   *
   *  using annotations = reflect_struct<MyStruct>::annotations;
   *
   *  // yields `fatal::sequence<char,
   *  //   's', 'o', 'm', 'e', ' ', 'v', 'a', 'l', 'u', 'e'
   *  // >`
   *  using result1 = annotations::values::some_annotation;
   *
   * @author: Marcelo Juchem <marcelo@fb.com>
   */
  using values = typename Metadata::values;

  /**
   * A type map representing the annotations declared in the Thrift file,
   * sorted by keys.
   *
   * Both the keys and the values of this map are compile-time strings
   * represented by `fatal::sequence` of type `char`.
   *
   * Example:
   *
   *  // yields an instantiation of the `reflected_annotations` template
   *  using annotations = reflect_struct<MyStruct>::annotations;
   *
   *  FATAL_S(key, "another.annotation");
   *
   *  // yields `fatal::sequence<char,
   *  //   'a', 'n', 'o', 't', 'h', 'e', 'r', ' ', 'v', 'a', 'l', 'u', 'e'
   *  // >`
   *  using result1 = annotations::map::get<key>;
   *
   *  // yields `fatal::sequence<char,
   *  //   's', 'o', 'm', 'e', ' ', 'v', 'a', 'l', 'u', 'e'
   *  // >`
   *  using result2 = annotations::map::get<annotations::keys::some_annotation>;
   *
   * @author: Marcelo Juchem <marcelo@fb.com>
   */
  using map = typename Metadata::map;
};

////////////////////////////
// SECTION: STRUCTURE API //
////////////////////////////

/**
 * Holds reflection metadata for a given struct.
 *
 * NOTE: this class template is only intended to be instantiated by Thrift.
 * Users should ignore the template parameters taken by it and focus simply on
 * the members provided.
 *
 * For the examples below, consider code generated for this Thrift file:
 *
 *  /////////////////////
 *  // MyModule.thrift //
 *  /////////////////////
 *  namespace cpp2 My.Namespace
 *
 *  struct MyStruct {
 *    1: i32 a
 *    2: string b
 *    3: double c (
 *      member.note = "member text",
 *      another.member.note = "another member text",
 *    )
 *  } (
 *    some.annotation = "some value",
 *    another.annotation = "another value",
 *  )
 *
 * @author: Marcelo Juchem <marcelo@fb.com>
 */
template <
  typename Struct,
  typename Name,
  typename MembersInfo,
  typename Info,
  typename MembersAnnotations,
  typename RequiredFields,
  typename NonRequiredFields,
  typename Metadata
>
struct reflected_struct {
  /**
   * A type alias for the struct itself.
   *
   * Example:
   *
   *  using info = reflect_struct<MyStruct>;
   *
   *  // yields `MyStruct`
   *  using result = info::type;
   *
   * @author: Marcelo Juchem <marcelo@fb.com>
   */
  using type = Struct;

  /**
   * A compile-time string representing the struct name.
   *
   * Example:
   *
   *  using info = reflect_struct<MyStruct>;
   *
   *  // yields `fatal::sequence<
   *  //   char,
   *  //   'M', 'y', 'S', 't', 'r', 'u', 'c', 't'
   *  // >`
   *  using result = info::name;
   *
   * @author: Marcelo Juchem <marcelo@fb.com>
   */
  using name = Name;

  /**
   * The reflection metadata tag for the Thrift file where this structure is
   * declared.
   *
   * Example:
   *
   *  using info = reflect_struct<MyStruct>;
   *
   *  // yields `My::Namespace::MyModule_tags::module`
   *  using result = info::module;
   *
   * @author: Marcelo Juchem <marcelo@fb.com>
   */
  using module = typename Metadata::module;

  /**
   * An implementation defined type template that provides the appropriate
   * `reflected_struct_data_member` for each data member as a member type alias
   * with the same name.
   *
   * These type aliases are used as the type mapped by the `members` member
   * offered by the `reflected_struct` class.
   *
   * An optional transform can be specified, which will be applied on top of the
   * members' `reflected_struct_data_member`.
   *
   * Example:
   *
   *  using info = reflect_struct<MyStruct>;
   *
   *  // yields `fatal::sequence<char, 'a'>`
   *  using result1 = info::member::a::name;
   *
   *  // yields `std::int32_t`
   *  using result2 = info::member::a::type;
   *
   *  // yields `1`
   *  using result3 = info::member::a::id::value;
   *
   * @author: Marcelo Juchem <marcelo@fb.com>
   */
  using member = MembersInfo;

  /**
   * A `fatal::map` from the data member name to the corresponding metadata
   * for that data member as a `reflected_struct_data_member`.
   *
   * See the documentation for `reflected_struct_data_member` (below) for more
   * information on its members.
   *
   * Example:
   *
   *  using info = reflect_struct<MyStruct>;
   *
   *  using member = info::member::b;
   *
   *  // yields "b"
   *  auto result1 = fatal::z_data<member::name>();
   *
   *  // yields `std::string`
   *  using result2 = member::type;
   *
   *  // yields `2`
   *  auto result3 = member::id::value;
   *
   * @author: Marcelo Juchem <marcelo@fb.com>
   */
  using members = Info;

  /**
   * An instantiation of `reflected_annotations` representing the annotations
   * declared for this type in the Thrift file.
   *
   * Example:
   *
   *  using info = reflect_struct<MyStruct>;
   *
   *  // yields `fatal::sequence<char,
   *  //   'a', 'n', 'o', 't', 'h', 'e', 'r', ' ', 'v', 'a', 'l', 'u', 'e'
   *  // >`
   *  using result = info::annotations::values::another_annotation;
   *
   * @author: Marcelo Juchem <marcelo@fb.com>
   */
  using annotations = typename Metadata::annotations;

  /**
   * An implementation defined type that provides the annotations for each
   * member of the struct. Each member's annotations are represented by an
   * instance of `reflected_annotations` named after the member itself.
   *
   * Example:
   *
   *  using info = reflect_struct<MyStruct>;
   *
   *  // yields `fatal::sequence<char,
   *  //   'm', 'e', 'm', 'b', 'e', 'r', ' ', 't', 'e', 'x', 't'
   *  // >`
   *  using result1 = info::members_annotations::c::values::member_note
   *
   * @author: Marcelo Juchem <marcelo@fb.com>
   */
  using members_annotations = MembersAnnotations;

  /**
   * A unique identifier generated by thrift for this structure.
   *
   * NOTE: this is a legacy feature and should be avoided on new code.
   *
   * @author: Marcelo Juchem <marcelo@fb.com>
   */
  using legacy_id = typename Metadata::legacy_id;

  using required_fields = RequiredFields;
  using nonrequired_fields = NonRequiredFields;
};

/**
 * Holds reflection metadata for a given struct's data member.
 *
 * NOTE: this class template is only intended to be instantiated by Thrift.
 * Users should ignore the template parameters taken by it and focus simply on
 * the members provided.
 *
 * @author: Marcelo Juchem <marcelo@fb.com>
 */
template <
  typename Name,
  typename Type,
  field_id_t Id,
  optionality Optionality,
  typename Getter,
  typename TypeClass,
  template <typename> class Pod,
  typename Annotations,
  typename Owner,
  bool HasIsSet
>
struct reflected_struct_data_member {
  /**
   * A `fatal::sequence` of `char` representing the data member name as
   * a compile-time string.
   *
   * Example:
   *
   *  // MyModule.thrift
   *
   *  namespace cpp2 My.Namespace
   *
   *  struct MyStruct {
   *    1: i32 fieldA
   *    2: string fieldB
   *    3: double fieldC
   *  }
   *
   *  // C++
   *
   *  using info = reflect_struct<MyStruct>;
   *  using member = info::member::fieldC;
   *
   *  // yields `fatal::sequence<char, 'f', 'i', 'e', 'l', 'd', 'C'>`
   *  using result1 = member::name;
   *
   *  // yields "fieldC"
   *  auto result2 = fatal::z_data<result1>();
   *
   * @author: Marcelo Juchem <marcelo@fb.com>
   */
  using name = Name;

  /**
   * The type of the data member.
   *
   * Example:
   *
   *  // MyModule.thrift
   *
   *  namespace cpp2 My.Namespace
   *
   *  struct MyStruct {
   *    1: i32 fieldA
   *    2: string fieldB
   *    3: double fieldC
   *  }
   *
   *  // C++
   *
   *  using info = reflect_struct<MyStruct>;
   *  using member = info::member::fieldC;
   *
   *  // yields `double`
   *  using result1 = member::type;
   *
   * @author: Marcelo Juchem <marcelo@fb.com>
   */
  using type = Type;

  /**
   * A `std::integral_constant` of type `field_id_t` representing the Thrift
   * field id for the data member.
   *
   * Example:
   *
   *  // MyModule.thrift
   *
   *  namespace cpp2 My.Namespace
   *
   *  struct MyStruct {
   *    1: i32 fieldA
   *    2: string fieldB
   *    3: double fieldC
   *  }
   *
   *  // C++
   *
   *  using info = reflect_struct<MyStruct>;
   *  using member = info::member::fieldC;
   *
   *  // yields `std::integral_constant<field_id_t, 3>`
   *  using result1 = member::id;
   *
   *  // yields `3`
   *  auto result2 = result1::value;
   *
   * @author: Marcelo Juchem <marcelo@fb.com>
   */
  using id = std::integral_constant<field_id_t, Id>;

  /**
   * A `std::integral_constant` of type `optionality` representing whether a
   * field is qualified as required or optional.
   *
   * Example:
   *
   *  // MyModule.thrift
   *
   *  namespace cpp2 My.Namespace
   *
   *  struct MyStruct {
   *    1: required i32 fieldA
   *    2: optional string fieldB
   *    3: double fieldC
   *  }
   *
   *  // C++
   *
   *  using info = reflect_struct<MyStruct>;
   *  using a = info::member::fieldA;
   *  using b = info::member::fieldB;
   *  using c = info::member::fieldC;
   *
   *  // yields `std::integral_constant<optionality, optionality::required>`
   *  using result1 = a::required;
   *
   *  // yields `std::integral_constant<optionality, optionality::optional>`
   *  using result2 = b::required;
   *
   *  // yields `std::integral_constant<
   *  //   optionality,
   *  //   optionality::required_of_writer
   *  // >`
   *  using result3 = c::required;
   *
   * @author: Marcelo Juchem <marcelo@fb.com>
   */
  using optional = std::integral_constant<optionality, Optionality>;

  /**
   * A type that works as a getter for the data member.
   *
   * See also Fatal's documentation on `FATAL_DATA_MEMBER_GETTER` in
   * `fatal/type/traits.h` for more information on how to make the most out of
   * the data member getter.
   *
   * Example:
   *
   *  // MyModule.thrift
   *
   *  namespace cpp2 My.Namespace
   *
   *  struct MyStruct {
   *    1: i32 fieldA
   *    2: string fieldB
   *    3: double fieldC
   *  }
   *
   *  // C++
   *
   *  using info = reflect_struct<MyStruct>;
   *  using member = info::member::fieldC;
   *  using getter = member::getter;
   *
   *  MyStruct pod;
   *
   *  pod.fieldC = 7.2;
   *
   *  // yields `7.2`
   *  auto result1 = getter::ref(pod);
   *
   *  // sets  `5.6` on `pod.fieldC`
   *  getter::ref(pod) = 5.6;
   *
   *  // yields `5.6`
   *  auto result2 = pod.fieldC;
   *
   * @author: Marcelo Juchem <marcelo@fb.com>
   */
  using getter = Getter;

  /**
   * The type class for this member.
   *
   * Example:
   *
   *  // MyModule.thrift
   *
   *  namespace cpp2 My.Namespace
   *
   *  struct MyStruct {
   *    1: i32 fieldA
   *    2: string fieldB
   *    3: double fieldC
   *  }
   *
   *  // C++
   *
   *  using info = reflect_struct<MyStruct>;
   *  using member = info::member::fieldC;
   *
   *  // yields `type_class::floating_point`
   *  using result1 = member::type_class;
   *
   * @author: Marcelo Juchem <marcelo@fb.com>
   */
  using type_class = TypeClass;

  /**
   * A POD (plain old data) that holds a single data member with the same name
   * as this member. The template parameter `T` defines the type of this POD's
   * sole member, and defaults to `type`.
   *
   * This is useful when you need to create a class with an API compatible to
   * the original Thrift struct.
   *
   * Example:
   *
   *  // MyModule.thrift
   *
   *  namespace cpp2 My.Namespace
   *
   *  struct MyStruct {
   *    1: i32 fieldA
   *    2: string fieldB
   *    3: double fieldC
   *  }
   *
   *  // C++
   *
   *  using info = reflect_struct<MyStruct>;
   *  using member = info::member::fieldC;
   *
   *  member::pod<> original;
   *
   *  // yields `double`
   *  using result1 = decltype(original.fieldC);
   *
   *  member::pod<bool> another;
   *
   *  // yields `bool`
   *  using result2 = decltype(another.fieldC);
   *
   * @author: Marcelo Juchem <marcelo@fb.com>
   */
  template <typename T = type>
  using pod = Pod<T>;

  /**
   * An instantiation of `reflected_annotations` representing the annotations
   * declared for this member in the Thrift file.
   *
   * Example:
   *
   *  // MyModule.thrift
   *
   *  namespace cpp2 My.Namespace
   *
   *  struct MyStruct {
   *    1: i32 fieldA
   *    2: string fieldB
   *    3: double fieldC (field.note = "some notes")
   *  }
   *
   *  // MyModule.cpp
   *
   *  using info = reflect_struct<MyStruct>;
   *  using member = info::member::fieldC;
   *
   *  // yields `fatal::sequence<char,
   *  //   's', 'o', 'm', 'e', ' ', 'n', 'o', 't', 'e', 's'
   *  // >`
   *  using result = member::annotations::values::field_note;
   *
   * @author: Marcelo Juchem <marcelo@fb.com>
   */
  using annotations = Annotations;

  /**
   * Checks whether the member represented by this metadata is set in the given
   * object.
   *
   * Example:
   *
   *  // MyModule.thrift
   *
   *  namespace cpp2 My.Namespace
   *
   *  struct MyStruct {
   *    1: optional i32 field
   *  }
   *
   *  // MyModule.cpp
   *
   *  using info = reflect_struct<MyStruct>;
   *  using member = info::member::field;
   *
   *  MyStruct pod;
   *
   *  // yields `false`
   *  bool result1 = member::is_set(pod);
   *
   *  pod.set_field(42);
   *
   *  // yields `true`
   *  bool result2 = member::is_set(pod);
   *
   * @author: Marcelo Juchem <marcelo@fb.com>
   */
  static bool is_set(Owner const &owner) {
    return detail::reflection_impl::is_set<
        Owner, getter, HasIsSet
      >::check(owner);
  }

  /**
   * Marks the member as being set on the parent object.
   *
   * Example:
   *
   *  // MyModule.thrift
   *
   *  namespace cpp2 My.Namespace
   *
   *  struct MyStruct {
   *    1: optional i32 field
   *  }
   *
   *  // MyModule.cpp
   *
   *  using info = reflect_struct<MyStruct>;
   *  using member = info::types::members<info::member::field::name>;
   *
   * MyStruct pod;
   *
   * // mark `field` as being set
   * member::mark_set(pod)
   *
   *
   * @author: Dylan Knutson <dymk@fb.com>
   */
  static void mark_set(Owner& owner) {
    detail::reflection_impl::mark_set<Owner, getter, HasIsSet>::mark(owner);
  }

  /**
   * Marks the member as being not set on the parent object.
   *
   * Example:
   *
   *  // MyModule.thrift
   *
   *  namespace cpp2 My.Namespace
   *
   *  struct MyStruct {
   *    1: optional i32 field
   *  }
   *
   *  // MyModule.cpp
   *
   *  using info = reflect_struct<MyStruct>;
   *  using member = info::types::members<info::member::field::name>;
   *
   * MyStruct pod;
   *
   * // mark `field` as being not set
   * member::unmark_set(pod)
   *
   *
   * @author: Dylan Knutson <dymk@fb.com>
   */
  static void unmark_set(Owner& owner) {
    detail::reflection_impl::unmark_set<Owner, getter, HasIsSet>::mark(owner);
  }
};

/**
 * Retrieves reflection metadata (as a `reflected_struct`) associated with the
 * given struct.
 *
 * If the given type is not a Thrift struct, or if there's no reflection
 * metadata available for it, compilation will fail.
 *
 * See the documentation on `reflected_struct` (above) for more information on
 * the returned type.
 *
 * Example:
 *
 *  /////////////////////
 *  // MyModule.thrift //
 *  /////////////////////
 *  namespace cpp2 My.Namespace
 *
 *  struct MyStruct {
 *    1: i32 a
 *    2: string b
 *    3: double c
 *  }
 *
 *  //////////////////
 *  // whatever.cpp //
 *  //////////////////
 *  using info = reflect_struct<My::Namespace::MyStruct>;
 *
 *  // yields `3`
 *  auto result = fatal::size<info::members>::value;
 *
 * @author: Marcelo Juchem <marcelo@fb.com>
 */
template <typename Struct>
using reflect_struct = fatal::registry_lookup<
  detail::struct_traits_metadata_tag,
  Struct
>;

/**
 * Retrieves reflection metadata (as a `reflected_struct`) associated with the
 * given struct.
 *
 * If the given type is not a Thrift struct, or if there's no reflection
 * metadata available for it, `Default` will be returned. If `Default` is not
 * specified, it defaults to void.
 *
 * See the documentation on `reflected_struct` (above) for more information on
 * the returned type.
 *
 * Example:
 *
 *  /////////////////////
 *  // MyModule.thrift //
 *  /////////////////////
 *  namespace cpp2 My.Namespace
 *
 *  struct MyStruct {
 *    1: i32 a
 *    2: string b
 *    3: double c
 *  }
 *
 *  //////////////////
 *  // whatever.cpp //
 *  //////////////////
 *  using info = reflect_struct<My::Namespace::MyStruct>;
 *
 *  // yields `3`
 *  auto result = fatal::size<info::members>::value;
 *
 * @author: Marcelo Juchem <marcelo@fb.com>
 */
template <typename Struct, typename Default = void>
using try_reflect_struct = fatal::try_registry_lookup<
  detail::struct_traits_metadata_tag,
  Struct,
  Default
>;

/**
 * Tells whether the given type is a Thrift struct with compile-time reflection
 * support.
 *
 * Example:
 *
 *  /////////////////////
 *  // MyModule.thrift //
 *  /////////////////////
 *  namespace cpp2 My.Namespace
 *
 *  struct MyStruct {
 *    1: i32 a
 *    2: string b
 *    3: double c
 *  }
 *
 *  enum MyEnum { a, b, c }
 *
 *  /////////////
 *  // foo.cpp //
 *  /////////////
 *
 *  // yields `std::true_type`
 *  using result1 = is_reflectable_struct<MyStruct>;
 *
 *  // yields `std::false_type`
 *  using result2 = is_reflectable_struct<MyEnum>;
 *
 *  // yields `std::false_type`
 *  using result3 = is_reflectable_struct<void>;
 *
 * @author: Marcelo Juchem <marcelo@fb.com>
 */
template <typename T>
using is_reflectable_struct = std::integral_constant<
  bool,
  !std::is_same<
    try_reflect_struct<T, void>,
    void
  >::value
>;

//////////////////////////////
// SECTION: ENUMERATION API //
//////////////////////////////

/**
 * Holds reflection metadata for a given enumeration.
 *
 * NOTE: this class template is only intended to be instantiated by Thrift.
 * Users should ignore the template parameters taken by it and focus simply on
 * the members provided.
 *
 * For the examples below, consider code generated for this Thrift file:
 *
 *  /////////////////////
 *  // MyModule.thrift //
 *  /////////////////////
 *  namespace cpp2 My.Namespace
 *
 *  enum MyEnum {
 *    a, b, c
 *  } (
 *    some.annotation = "some value",
 *    another.annotation = "another value",
 *  )
 *
 * @author: Marcelo Juchem <marcelo@fb.com>
 */
template <typename T>
struct reflected_enum {
  /**
   * A type alias for the enumeration itself.
   *
   * Example:
   *
   *  using info = reflect_enum<MyEnum>;
   *
   *  // yields `MyEnum`
   *  using result = info::type;
   *
   * @author: Marcelo Juchem <marcelo@fb.com>
   */
  using type = T;

  /**
   * An alias to `fatal::enum_traits`.
   *
   * See `fatal::enum_traits`, from the Fatal library, for more information.
   *
   * Example:
   *
   *  using info = reflect_enum<MyEnum>;
   *  using traits = info::traits;
   *
   *  // yields "a"
   *  auto result = traits::to_string(MyEnum::a);
   *
   * @author: Marcelo Juchem <marcelo@fb.com>
   */
  using traits = fatal::enum_traits<type>;

  /**
   * The reflection metadata tag for the Thrift file where this enumeration is
   * declared.
   *
   * Example:
   *
   *  using info = reflect_enum<MyEnum>;
   *
   *  // yields `My::Namespace::MyModule_tags::module`
   *  using result = info::module;
   *
   * @author: Marcelo Juchem <marcelo@fb.com>
   */
  using module = typename traits::metadata::module;

  /**
   * An instantiation of `reflected_annotations` representing the annotations
   * declared for this type in the Thrift file.
   *
   * Example:
   *
   *  using info = reflect_enum<MyEnum>;
   *
   *  // yields `fatal::sequence<char,
   *  //   'a', 'n', 'o', 't', 'h', 'e', 'r', ' ', 'v', 'a', 'l', 'u', 'e'
   *  // >`
   *  using result = info::annotations::values::another_annotation;
   *
   * @author: Marcelo Juchem <marcelo@fb.com>
   */
  using annotations = typename traits::metadata::annotations;

  /**
   * A unique identifier generated by thrift for this structure.
   *
   * NOTE: this is a legacy feature and should be avoided on new code.
   *
   * @author: Marcelo Juchem <marcelo@fb.com>
   */
  using legacy_id = typename traits::metadata::legacy_id;
};

/**
 * Retrieves reflection metadata (as a `reflected_enum`) associated with the
 * given enumeration.
 *
 * If the given type is not a Thrift enumeration, or if there's no reflection
 * metadata available for it, compilation will fail.
 *
 * See the documentation on `reflected_enum` (above) for more information on
 * the returned type.
 *
 * Example:
 *
 *  /////////////////////
 *  // MyModule.thrift //
 *  /////////////////////
 *  namespace cpp2 My.Namespace
 *
 *  enum MyEnum { a, b, c }
 *
 *  //////////////////
 *  // whatever.cpp //
 *  //////////////////
 *  using info = reflect_enum<My::Namespace::MyEnum>;
 *
 *  // yields `MyEnum`
 *  auto result = info::type;
 *
 * @author: Marcelo Juchem <marcelo@fb.com>
 */
template <typename T>
using reflect_enum = reflected_enum<T>;

/**
 * Tells whether the given type is a Thrift enum with compile-time reflection
 * support.
 *
 * Example:
 *
 *  /////////////////////
 *  // MyModule.thrift //
 *  /////////////////////
 *  namespace cpp2 My.Namespace
 *
 *  union MyUnion {
 *    1: i32 a
 *    2: string b
 *    3: double c
 *  }
 *
 *  enum MyEnum { a, b, c }
 *
 *  /////////////
 *  // foo.cpp //
 *  /////////////
 *
 *  // yields `std::true_type`
 *  using result1 = is_reflectable_enum<MyEnum>;
 *
 *  // yields `std::false_type`
 *  using result2 = is_reflectable_enum<MyUnion>;
 *
 *  // yields `std::false_type`
 *  using result3 = is_reflectable_enum<void>;
 *
 * @author: Marcelo Juchem <marcelo@fb.com>
 */
template <typename T>
using is_reflectable_enum = fatal::has_enum_traits<T>;

//////////////////////////////////
// SECTION: VARIANT (UNION) API //
//////////////////////////////////

/**
 * Holds reflection metadata for a given union.
 *
 * NOTE: this class template is only intended to be instantiated by Thrift.
 * Users should ignore the template parameters taken by it and focus simply on
 * the members provided.
 *
 * For the examples below, consider code generated for this Thrift file:
 *
 *  /////////////////////
 *  // MyModule.thrift //
 *  /////////////////////
 *  namespace cpp2 My.Namespace
 *
 *  union MyUnion {
 *    1: i32 a
 *    2: string b
 *    3: double c
 *  } (
 *    some.annotation = "some value",
 *    another.annotation = "another value",
 *  )
 *
 * @author: Marcelo Juchem <marcelo@fb.com>
 */
template <typename T>
struct reflected_union {
  /**
   * A type alias for the union itself.
   *
   * Example:
   *
   *  using info = reflect_union<MyUnion>;
   *
   *  // yields `MyUnion`
   *  using result = info::type;
   *
   * @author: Marcelo Juchem <marcelo@fb.com>
   */
  using type = T;

  /**
   * An alias to `fatal::variant_traits`.
   *
   * See `fatal::variant_traits`, from the Fatal library, for more information.
   *
   * Example:
   *
   *  using info = reflect_union<MyUnion>;
   *  using traits = info::traits;
   *
   *  // yields `MyUnion::Type::a`
   *  auto result = traits::array::ids::get[0];
   *
   * @author: Marcelo Juchem <marcelo@fb.com>
   */
  using traits = fatal::variant_traits<type>;

  /**
   * The reflection metadata tag for the Thrift file where this union is
   * declared.
   *
   * Example:
   *
   *  using info = reflect_union<MyUnion>;
   *
   *  // yields `My::Namespace::MyModule_tags::module`
   *  using result = info::module;
   *
   * @author: Marcelo Juchem <marcelo@fb.com>
   */
  using module = typename traits::metadata::module;

  /**
   * An instantiation of `reflected_annotations` representing the annotations
   * declared for this type in the Thrift file.
   *
   * Example:
   *
   *  using info = reflect_union<MyUnion>;
   *
   *  // yields `fatal::sequence<char,
   *  //   'a', 'n', 'o', 't', 'h', 'e', 'r', ' ', 'v', 'a', 'l', 'u', 'e'
   *  // >`
   *  using result = info::annotations::values::another_annotation;
   *
   * @author: Marcelo Juchem <marcelo@fb.com>
   */
  using annotations = typename traits::metadata::annotations;

  /**
   * A unique identifier generated by thrift for this structure.
   *
   * NOTE: this is a legacy feature and should be avoided on new code.
   *
   * @author: Marcelo Juchem <marcelo@fb.com>
   */
  using legacy_id = typename traits::metadata::legacy_id;
};

/**
 * Retrieves reflection metadata (as a `reflected_union`) associated with the
 * given union.
 *
 * If the given type is not a Thrift union, or if there's no reflection
 * metadata available for it, compilation will fail.
 *
 * See the documentation on `reflected_union` (above) for more information on
 * the returned type.
 *
 * Example:
 *
 *  /////////////////////
 *  // MyModule.thrift //
 *  /////////////////////
 *  namespace cpp2 My.Namespace
 *
 *  union MyUnion {
 *    1: i32 a
 *    2: string b
 *    3: double c
 *  }
 *
 *  //////////////////
 *  // whatever.cpp //
 *  //////////////////
 *  using info = reflect_union<My::Namespace::MyUnion>;
 *
 *  // yields `MyUnion`
 *  auto result = info::type;
 *
 * @author: Marcelo Juchem <marcelo@fb.com>
 */
template <typename T>
using reflect_union = reflected_union<T>;

/**
 * Represents Thrift specific metadata for a given union's member.
 *
 * This is exposed as the metadata for Fatal's `variant_type_descriptor`.
 *
 * For the examples below, consider code generated for this Thrift file:
 *
 *  /////////////////////
 *  // MyModule.thrift //
 *  /////////////////////
 *  namespace cpp2 My.Namespace
 *
 *  union MyUnion {
 *    1: i32 a
 *    2: string b
 *    3: double c
 *  }
 *
 * Example:
 *
 *  //////////////////
 *  // whatever.cpp //
 *  //////////////////
 *  using info = reflect_union<My::Namespace::MyUnion>;
 *  using metadata = info::by_type<double>::metadata;
 *
 *  // yields "c"
 *  auto result = fatal::z_data<metadata::name>();
 *
 * @author: Marcelo Juchem <marcelo@fb.com>
 */
template <typename Name, field_id_t Id, typename TypeClass>
struct reflected_union_member_metadata {
  /**
   * A compile-time string representing the name of this member.
   *
   * Example:
   *
   *  using info = reflect_union<My::Namespace::MyUnion>;
   *
   *  // yields "c"
   *  auto result = fatal::z_data<info::by_type<double>::metadata::name>();
   *
   * @author: Marcelo Juchem <marcelo@fb.com>
   */
  using name = Name;

  /**
   * A `std::integral_constant` of type `field_id_t` representing the Thrift
   * field id for this member.
   *
   * Example:
   *
   *  using info = reflect_union<My::Namespace::MyUnion>;
   *
   *  // yields `3`
   *  auto result = info::by_type<double>::metadata::id::value;
   *
   * @author: Marcelo Juchem <marcelo@fb.com>
   */
  using id = std::integral_constant<field_id_t, Id>;

  /**
   * The type class for this member.
   *
   * Example:
   *
   *  using info = reflect_union<My::Namespace::MyUnion>;
   *
   *  // yields `type_class::floating_point`
   *  auto result = info::by_type<double>::metadata::id;
   *
   * @author: Marcelo Juchem <marcelo@fb.com>
   */
  using type_class = TypeClass;
};

/**
 * Tells whether the given type is a Thrift union with compile-time reflection
 * support.
 *
 * Example:
 *
 *  /////////////////////
 *  // MyModule.thrift //
 *  /////////////////////
 *  namespace cpp2 My.Namespace
 *
 *  union MyUnion {
 *    1: i32 a
 *    2: string b
 *    3: double c
 *  }
 *
 *  enum MyEnum { a, b, c }
 *
 *  /////////////
 *  // foo.cpp //
 *  /////////////
 *
 *  // yields `std::true_type`
 *  using result1 = is_reflectable_union<MyUnion>;
 *
 *  // yields `std::false_type`
 *  using result2 = is_reflectable_union<MyEnum>;
 *
 *  // yields `std::false_type`
 *  using result3 = is_reflectable_union<void>;
 *
 * @author: Marcelo Juchem <marcelo@fb.com>
 */
template <typename T>
using is_reflectable_union = fatal::has_variant_traits<T>;

//////////////////////////
// CONTAINER TRAITS API //
//////////////////////////

/**
 * This is the type trait class that provides uniform interface to the
 * properties and functionality of string types.
 *
 * To make a custom string type available to Thrift's compile-time reflection
 * support, specialize this template for it.
 *
 * To keep compilation times at bay, standard strings are not supported by
 * default, therefore they will yield an undefined `thrift_string_traits`.
 * To enable their support, include `container_traits.h`. There's also support
 * for containers defined in Folly at `container_traits_folly.h`.
 *
 * This is the specialization for standard strings, showing what's offered by
 * the `thrift_string_traits` class. It can also be used as a template when
 * adding support for custom string classes:
 *
 *  template <typename C, typename T, typename A>
 *  struct thrift_string_traits<std::basic_string<C, T, A>> {
 *    using type = std::basic_string<C, T, A>;
 *
 *    using value_type = typename type::value_type;
 *    using size_type = typename type::size_type;
 *    using iterator = typename type::iterator;
 *    using const_iterator = typename type::const_iterator;
 *
 *    static iterator begin(type &what) { return what.begin(); }
 *    static iterator end(type &what) { return what.end(); }
 *
 *    static const_iterator cbegin(type const &what) { return what.cbegin(); }
 *    static const_iterator begin(type const &what) { return what.cbegin(); }
 *    static const_iterator cend(type const &what) { return what.end(); }
 *    static const_iterator end(type const &what) { return what.end(); }
 *
 *    static void clear(type &what) { what.clear(); }
 *    static bool empty(type const &what) { return what.empty(); }
 *    static size_type size(type const &what) { return what.size(); }
 *
 *    static value_type const *data(type const &what) { return what.data(); }
 *    static value_type const *c_str(type const &what) { return what.c_str(); }
 *  };
 *
 * @author: Marcelo Juchem <marcelo@fb.com>
 */
template <typename> struct thrift_string_traits;

template <typename String>
struct thrift_string_traits_std {
  using type = String;

  using value_type = typename type::value_type;
  using size_type = typename type::size_type;
  using iterator = typename type::iterator;
  using const_iterator = typename type::const_iterator;

  static iterator begin(type &what) { return what.begin(); }
  static iterator end(type &what) { return what.end(); }

  static const_iterator cbegin(type const &what) { return what.cbegin(); }
  static const_iterator begin(type const &what) { return what.begin(); }
  static const_iterator cend(type const &what) { return what.cend(); }
  static const_iterator end(type const &what) { return what.end(); }

  static void clear(type &what) { what.clear(); }
  static bool empty(type const &what) { return what.empty(); }
  static size_type size(type const &what) { return what.size(); }

  static value_type const *data(type const &what) { return what.data(); }
  static value_type const *c_str(type const &what) { return what.c_str(); }
};

/**
 * This is the type trait class that provides uniform interface to the
 * properties and functionality of list types.
 *
 * To make a custom list type available to Thrift's compile-time reflection
 * support, specialize this template for it.
 *
 * To keep compilation times at bay, standard lists are not supported by
 * default, therefore they will yield an undefined `thrift_list_traits`.
 * To enable their support, include `container_traits.h`. There's also support
 * for containers defined in Folly at `container_traits_folly.h`.
 *
 * This is the specialization for standard vector, showing what's offered by
 * the `thrift_list_traits` class. It can also be used as a template when
 * adding support for custom list classes:
 *
 *  template <typename T, typename A>
 *  struct thrift_list_traits<std::vector<T, A>> {
 *    using type = std::vector<T, A>;
 *
 *    using value_type = typename type::value_type;
 *    using size_type = typename type::size_type;
 *    using iterator = typename type::iterator;
 *    using const_iterator = typename type::const_iterator;
 *
 *    static iterator begin(type &what) { return what.begin(); }
 *    static iterator end(type &what) { return what.end(); }
 *
 *    static const_iterator cbegin(type const &what) { return what.cbegin(); }
 *    static const_iterator begin(type const &what) { return what.begin(); }
 *    static const_iterator cend(type const &what) { return what.cend(); }
 *    static const_iterator end(type const &what) { return what.end(); }
 *
 *    static void clear(type &what) { what.clear(); }
 *    static bool empty(type const &what) { return what.empty(); }
 *    static void push_back(type &what, value_type const &e) {
 *      what.push_back(e); }
 *    static void push_back(type &what, value_type &&e) {
 *      what.push_back(std::move(e)); }
 *    static void reserve(type &what, size_type size) { what.reserve(size); }
 *    static size_type size(type const &what) { return what.size(); }
 *  };
 *
 * @author: Marcelo Juchem <marcelo@fb.com>
 */
template <typename> struct thrift_list_traits;

template <typename List>
struct thrift_list_traits_std {
  using type = List;

  using value_type = typename type::value_type;
  using size_type = typename type::size_type;
  using iterator = typename type::iterator;
  using const_iterator = typename type::const_iterator;

  static iterator begin(type &what) { return what.begin(); }
  static iterator end(type &what) { return what.end(); }

  static const_iterator cbegin(type const &what) { return what.cbegin(); }
  static const_iterator begin(type const &what) { return what.begin(); }
  static const_iterator cend(type const &what) { return what.cend(); }
  static const_iterator end(type const &what) { return what.end(); }

  static void clear(type &what) { what.clear(); }
  static bool empty(type const &what) { return what.empty(); }
  static void push_back(type &what, value_type const &val) {
    what.push_back(val);
  }
  static void push_back(type &what, value_type &&val) {
    what.push_back(std::move(val));
  }
  static void reserve(type &what, size_type size) { what.reserve(size); }
  static size_type size(type const &what) { return what.size(); }
};

/**
 * This is the type trait class that provides uniform interface to the
 * properties and functionality of set types.
 *
 * To make a custom set type available to Thrift's compile-time reflection
 * support, specialize this template for it.
 *
 * To keep compilation times at bay, standard sets are not supported by
 * default, therefore they will yield an undefined `thrift_set_traits`.
 * To enable their support, include `container_traits.h`. There's also support
 * for containers defined in Folly at `container_traits_folly.h`.
 *
 * This is the specialization for standard sets, showing what's offered by
 * the `thrift_set_traits` class. It can also be used as a template when
 * adding support for custom set classes:
 *
 *  template <typename K, typename H, typename E, typename A>
 *  struct thrift_set_traits<std::unordered_set<K, H, E, A>> {
 *    using type = std::unordered_set<K, H, E, A>;
 *
 *    using key_type = typename type::key_type;
 *    using value_type = typename type::value_type;
 *    using size_type = typename type::size_type;
 *    using iterator = typename type::iterator;
 *    using const_iterator = typename type::const_iterator;
 *
 *    using value_const_reference = value_type const &;
 *    using value_reference = value_type &;
 *
 *    static iterator begin(type &what) { return what.begin(); }
 *    static iterator end(type &what) { return what.end(); }
 *
 *    static const_iterator cbegin(type const &what) { return what.cbegin(); }
 *    static const_iterator begin(type const &what) { return what.begin(); }
 *    static const_iterator cend(type const &what) { return what.cend(); }
 *    static const_iterator end(type const &what) { return what.end(); }
 *
 *    static void clear(type &what) { what.clear(); }
 *    static bool empty(type const &what) { return what.empty(); }
 *    static iterator find(type &what, value_type const &val) {
 *      return what.find(val); }
 *    static const_iterator find(type const &what, value_type const &val) {
 *      return what.find(val); }
 *    static iterator insert(
 *        type &what, const_iterator position, value_type const &val) {
 *      return what.insert(position, val); }
 *    static iterator insert(
 *        type &what, const_iterator position, value_type &&val) {
 *      return what.insert(position, std::move(e)); }
 *    static size_type size(type const &what) { return what.size(); }
 *  };
 *
 * @author: Marcelo Juchem <marcelo@fb.com>
 */
template <typename> struct thrift_set_traits;

template <typename Set>
struct thrift_set_traits_std {
  using type = Set;

  using key_type = typename type::key_type;
  using value_type = typename type::value_type;
  using size_type = typename type::size_type;
  using iterator = typename type::iterator;
  using const_iterator = typename type::const_iterator;

  static iterator begin(type &what) { return what.begin(); }
  static iterator end(type &what) { return what.end(); }

  static const_iterator cbegin(type const &what) { return what.cbegin(); }
  static const_iterator begin(type const &what) { return what.begin(); }
  static const_iterator cend(type const &what) { return what.cend(); }
  static const_iterator end(type const &what) { return what.end(); }

  static void clear(type &what) { what.clear(); }
  static bool empty(type const &what) { return what.empty(); }
  static iterator find(type &what, value_type const &val) {
    return what.find(val);
  }
  static const_iterator find(type const &what, value_type const &val) {
    return what.find(val);
  }
  static iterator insert(
      type &what, const_iterator position, value_type const &val) {
    return what.insert(position, val);
  }
  static iterator insert(
      type &what, const_iterator position, value_type &&val) {
    return what.insert(position, std::move(val));
  }
  static size_type size(type const &what) { return what.size(); }
};

/**
 * This is the type trait class that provides uniform interface to the
 * properties and functionality of map types.
 *
 * To make a custom map type available to Thrift's compile-time reflection
 * support, specialize this template for it.
 *
 * To keep compilation times at bay, standard maps are not supported by
 * default, therefore they will yield an undefined `thrift_map_traits`.
 * To enable their support, include `container_traits.h`. There's also support
 * for containers defined in Folly at `container_traits_folly.h`.
 *
 * This is the specialization for standard maps, showing what's offered by
 * the `thrift_map_traits` class. It can also be used as a template when
 * adding support for custom map classes:
 *
 *  template <typename K, typename T, typename H, typename E, typename A>
 *  struct thrift_map_traits<std::unordered_map<K, T, H, E, A>> {
 *    using type = std::unordered_map<K, T, H, E, A>;
 *
 *    using key_type = typename type::key_type;
 *    using mapped_type = typename type::mapped_type;
 *    using value_type = typename type::value_type;
 *    using size_type = typename type::size_type;
 *    using iterator = typename type::iterator;
 *    using const_iterator = typename type::const_iterator;
 *
 *    using key_const_reference = key_type const &;
 *    using mapped_const_reference = mapped_type const &;
 *    using mapped_reference = mapped_type &;
 *
 *    static iterator begin(type &what) { return what.begin(); }
 *    static iterator end(type &what) { return what.end(); }
 *
 *    static const_iterator cbegin(type const &what) { return what.cbegin(); }
 *    static const_iterator begin(type const &what) { return what.begin(); }
 *    static const_iterator cend(type const &what) { return what.cend(); }
 *    static const_iterator end(type const &what) { return what.end(); }
 *
 *    static key_const_reference key(const_iterator i) { return i->first; }
 *    static key_const_reference key(iterator i) { return i->first; }
 *    static mapped_const_reference mapped(const_iterator i) {
 *      return i->second; }
 *    static mapped_reference mapped(iterator i) { return i->second; }
 *
 *    static void clear(type &what) { what.clear(); }
 *    static bool empty(type const &what) { return what.empty(); }
 *    static iterator find(type &what, key_type const &k) {
 *      return what.find(k); }
 *    static const_iterator find(type const &what, key_type const &k) {
 *      return what.find(k); }
 *    static mapped_type& get_or_create(type &what, key_type const &k) {
 *      return what[k]; }
 *    static mapped_type& get_or_create(type &what, key_type &&k) {
 *      return what[std::move(k)]; }
 *    static size_type size(type const &what) { return what.size(); }
 *  };
 *
 * @author: Marcelo Juchem <marcelo@fb.com>
 */
template <typename> struct thrift_map_traits;

template <typename Map>
struct thrift_map_traits_std {
  using type = Map;

  using key_type = typename type::key_type;
  using mapped_type = typename type::mapped_type;
  using value_type = typename type::value_type;
  using size_type = typename type::size_type;
  using iterator = typename type::iterator;
  using const_iterator = typename type::const_iterator;

  using key_const_reference = key_type const &;
  using mapped_const_reference = mapped_type const &;
  using mapped_reference = mapped_type &;

  static iterator begin(type &what) { return what.begin(); }
  static iterator end(type &what) { return what.end(); }

  static const_iterator cbegin(type const &what) { return what.cbegin(); }
  static const_iterator begin(type const &what) { return what.begin(); }
  static const_iterator cend(type const &what) { return what.cend(); }
  static const_iterator end(type const &what) { return what.end(); }

  static key_const_reference key(const_iterator i) { return i->first; }
  static key_const_reference key(iterator i) { return i->first; }
  static mapped_const_reference mapped(const_iterator i) { return i->second; }
  static mapped_reference mapped(iterator i) { return i->second; }

  static void clear(type &what) { what.clear(); }
  static bool empty(type const &what) { return what.empty(); }
  static iterator find(type &what, key_type const &k) { return what.find(k); }
  static const_iterator find(type const &what, key_type const &k) {
    return what.find(k);
  }
  static mapped_type& get_or_create(type &what, key_type const &k) {
    return what[k];
  }
  static mapped_type& get_or_create(type &what, key_type &&k) {
    return what[std::move(k)];
  }
  static size_type size(type const &what) { return what.size(); }
};

}} // apache::thrift

#include <thrift/lib/cpp2/fatal/internal/reflection-inl-post.h>

#endif // THRIFT_FATAL_REFLECTION_H_
